#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/signal.h>
#include <linux/rcupdate.h>
#include <linux/delay.h>
#include <linux/kthread.h>

/*******************




[248771.621958] test_rcu_init iValues = 10
[248771.823978] myrcu_reader_thread, read x = 0
[248772.024008] myrcu_writer_thread write to new value = 10
[248772.028147] myrcu_reader_thread, read x = 10
[248772.043992] myrcu_del  x = 0
[248772.231994] myrcu_reader_thread, read x = 10
[248772.428013] myrcu_writer_thread write to new value = 11
[248772.436012] myrcu_reader_thread, read x = 11
[248772.444030] myrcu_del  x = 10
[248772.640042] myrcu_reader_thread, read x = 11
[248772.832023] myrcu_writer_thread write to new value = 12
[248772.844036] myrcu_reader_thread, read x = 12
[248772.848089] myrcu_del  x = 11
[248773.048042] myrcu_reader_thread, read x = 12








*************************/



struct Footype{
	int x;
	struct rcu_head rcu;
};

static struct Footype *g_ptr;


static void myrcu_del(struct rcu_head *rh)
{
	struct Footype *p = container_of(rh, struct Footype, rcu);
	printk("%s  x = %d \n",__func__,p->x);
	kfree(p);
}

static int myrcu_writer_thread(void *p)
{
	struct Footype *new_ptr;
	struct Footype *old;
	int value = (unsigned long)p;
	
	while(1){
		msleep(400);
		new_ptr = kmalloc(sizeof(struct Footype ),GFP_KERNEL);
		old = g_ptr;
		printk("%s write to new value = %d\n",__func__,value);
		*new_ptr = *old;
		new_ptr->x = value;
		rcu_assign_pointer(g_ptr,new_ptr);
		call_rcu(&old->rcu,myrcu_del);
		value++;
	}
	return 0;
}

static int myrcu_reader_thread(void*data)
{
	struct Footype *p = NULL;
	
	while(1){
		msleep(200);
		rcu_read_lock();
		p = rcu_dereference(g_ptr);
		if(p)
			printk("%s, read x = %d \n",__func__,p->x);
		rcu_read_unlock();
	}
	return 0;
}

static int __init test_rcu_init(void)
{
	struct task_struct *writer_thread;
	struct task_struct *reader_thread;
//	char str[100] = "hello";
	int iValues = 10;

	printk("%s iValues = %d\n",__func__, iValues);

	g_ptr = kzalloc(sizeof(struct Footype),GFP_KERNEL);
	if(!g_ptr)
		{
			kfree(g_ptr);
			
			printk("free\n");
			return -1;
		}
	
	writer_thread = kthread_run(myrcu_writer_thread,(void *)(unsigned long)iValues,"rcu_writer");
	reader_thread = kthread_run(myrcu_reader_thread, NULL ,"rcu_reader");
	return 0;
}

static void __exit test_rcu_exit(void)
{
	printk("%s\n",__func__);
	if(g_ptr)
		kfree(g_ptr);
}


MODULE_LICENSE("GPL");
module_init(test_rcu_init);
module_exit(test_rcu_exit);

MODULE_AUTHOR("biaowang");
